﻿<!DOCTYPE html>
<html>
<head>
    <title>HeartVolumeRenderV22</title>

    <!--引入所需的JavaScript文件-->
    <script src="jquery-3.1.0.js" type="text/javascript"></script>
    <script src="stats.min.js" type="text/javascript"></script>
    <script src="MainProgram.js" type="text/javascript"></script>
    <script src="GLManager.js" type="text/javascript"></script>
    <script src="LayoutManager.js" type="text/javascript"></script>
    <script src="UIFunctions.js" type="text/javascript"></script>
    <script src="FileLoadFuncs.js" type="text/javascript"></script>
    <script src="TransFuncManager.js" type="text/javascript"></script>
    <script src="ExtendFunctions.js" type="text/javascript"></script>
    <script src="Vector3.js" type="text/javascript"></script>

    <!--顶点着色器代码-->
    <script id="VSCode" type="x-shader/x-vertex">
        attribute vec3 position;            //从程序中传过来的位置数据
        varying   vec2 ScreenPos;           //屏幕位置，将要传递给片段着色器

        void main(void)
        {
	        ScreenPos = vec2(position.x/2.0,position.y/2.0);            //将[-1,1]的范围变为[-0.5,0.5]
	        gl_Position   = vec4(position, 1.0);                        //gl_Position不做变化
        }
	</script>

    <!--片段着色器代码-->
    <script id="FSCode" type="x-shader/x-fragment">
        precision mediump float;
        varying vec2 ScreenPos;             //屏幕位置，从顶点着色器收到

        uniform float testValue;            //测试数值
        uniform sampler2D volumeData;       //体数据！（因为webGL暂时不支持3D纹理，只能把3D拆分成一个大的2D纹理）
        uniform sampler2D TransFuncData;    //传递函数的数据纹理
        uniform float sampleType;           //采样方式（临近或线性）
        uniform float step;                 //步长
        uniform float GlobalOpacity;        //全局透明度调整
        uniform vec3 VolDataSize;           //体数据尺寸，xy是单元宽高所占比例，z是层数
        uniform vec3 EyePos;                //观察点位置
        uniform vec3 ViewXAxis;             //观察坐标系X轴（代表观察方向的正右侧）
        uniform vec3 ViewYAxis;             //观察坐标系y轴（代表观察方向的正上侧）
        uniform vec3 ViewZAxis;             //观察坐标系z轴（代表观察方向）
        uniform float SurfaceIncreaseEnable;//启用表面增强
        uniform float SurfaceIncreaseValue; //表面界定值
        uniform float LightEnable;          //启用光照效果
        uniform vec3 LightIntensity;        //光照强度（xyz分别是环境光，漫反射，镜面反射）
        uniform vec3 LightPos;              //光照方向
        uniform float ContPreEnable;        //启用环境保留效果
        uniform float ContPre_Kt;           //环境保留效果的Kt参数
        uniform float ContPre_Ks;           //环境保留效果的Ks参数
        uniform float FresnelEnable;        //启用菲涅尔效果
        uniform float FresnelValue;         //菲涅尔系数
        uniform vec3 FresnelColor;           //菲尼尔颜色

        vec4 SMP3D_Near(float x, float y, float z)  //临近采样3D纹理
        {
            float XDivid = floor(1.0/VolDataSize.x);
            float index = VolDataSize.z*z;
            index =floor(index);
            float xMove = mod(index,XDivid);
            float yMove = floor(index/XDivid);

            return texture2D(volumeData, vec2((x+xMove)*VolDataSize.x,(y+yMove)*VolDataSize.y));
        }

        vec4 SMP3D_Line(float x, float y, float z)  //线性采样3D纹理
        {
            float XDivid = floor(1.0/VolDataSize.x);
            float mid = VolDataSize.z*z;
            float left =floor(mid);
            float right = left+1.0;

            float xMove = mod(left,XDivid);
            float yMove = floor(left/XDivid);
            vec4 ColorLeft = texture2D(volumeData, vec2((x+xMove)*VolDataSize.x,(y+yMove)*VolDataSize.y));

            xMove = mod(right,XDivid);
            yMove = floor(right/XDivid);
            vec4 ColorRight = texture2D(volumeData, vec2((x+xMove)*VolDataSize.x,(y+yMove)*VolDataSize.y));

            float k = mid-left;

            return ColorLeft*(1.0-k) + ColorRight*k;
        }


        //以下是为了实现特殊效果的函数

        float SurfaceIncreaseFunc(vec3 grad)    //表面增强
        {
            float s = (grad.x*grad.x+grad.y*grad.y+grad.z*grad.z);
            return s*SurfaceIncreaseValue;
        }

        float Light(vec3 norm,vec3 NowPos)          //光照
        {
            float ambient = LightIntensity.x;
            float diffuse = dot(norm,LightPos)*LightIntensity.y;
            if(diffuse < 0.0)
                return ambient;
            float specular = dot(reflect(-LightPos,norm),normalize(EyePos-NowPos))*LightIntensity.z;
            if(specular<0.0)
                return ambient+diffuse;
            return ambient+diffuse+specular;
        }

        float ContPre(vec3 gradient,float intensity, float distancePE, float previousAlpha)  //环境保留Context-preserving
        {
            distancePE = (distancePE-(3.0-sqrt(3.0)/2.0))/sqrt(3.0);            //将值限制在0~1中
            float exponent = pow(ContPre_Kt*intensity*(1.0-distancePE)*(1.0-previousAlpha),ContPre_Ks);
            return pow(sqrt((gradient.x*gradient.x+gradient.y*gradient.y+gradient.z*gradient.z)/3.0),exponent);
        }

        vec3 Fresnel(vec3 color,vec3 norm,vec3 EyeDir)
        {
            float f = 1.0-abs(dot(norm,normalize(EyeDir)));
            if(f < FresnelValue)
                return color;
            else
            {
                float k=(f-FresnelValue)/(1.0-FresnelValue);
                return color*(1.0-k)+FresnelColor*k;
            }

        }

        //主函数，计算最终颜色！
        void main(void)
        {
	    vec3 debug_BackGroundColor = vec3(1.0,1.0,1.0);
            //以下代码为渲染核心代码
            vec3 Dir = normalize((ScreenPos.x)*ViewXAxis+(ScreenPos.y)*ViewYAxis+2.0*ViewZAxis);//根据这个点的屏幕位置计算出这条视线的方向
            //**************************射线碰撞检测算法
            vec3 boxmin = vec3(-0.5,-0.5,-0.5);
            vec3 boxmax = vec3( 0.5, 0.5, 0.5);
            vec3 invR = vec3(1.0/Dir.x,1.0/Dir.y,1.0/Dir.z);
            vec3 tbot = vec3( invR.x*(boxmin - EyePos).x, invR.y*(boxmin - EyePos).y, invR.z*(boxmin - EyePos).z);
            vec3 ttop = vec3( invR.x*(boxmax - EyePos).x, invR.y*(boxmax - EyePos).y, invR.z*(boxmax - EyePos).z);

            vec3 tmin = vec3(min(ttop.x, tbot.x), min(ttop.y, tbot.y), min(ttop.z, tbot.z));
            vec3 tmax = vec3(max(ttop.x, tbot.x), max(ttop.y, tbot.y), max(ttop.z, tbot.z));

            float near = max(max(tmin.x, tmin.y), max(tmin.x, tmin.z));
            float far = min(min(tmax.x, tmax.y), min(tmax.x, tmax.z));

            if(far < near)      //如果far<near则说明这条射线与我们的体数据立方体不相交，则直接将当前计算的像素变为黑色并终止这个像素的运算
            {
                gl_FragColor=vec4(debug_BackGroundColor,1.0);
                return ;
            }
            //**************************射线碰撞检测算法
            //**************************如果进行到这一步说明这条射线与我们的体数据立方体相交，则开始正式计算颜色

            vec4 AccumulateColor = vec4(0.0,0.0,0.0,0.0);   //最终叠加的颜色
            vec3 NowPos = EyePos + near*Dir;                //当前计算的位置，
            for(int i=0 ;i<2000;i++)
            {
                vec3 smpPos=NowPos+vec3(0.5,0.5,0.5);       //采样点位置，之所以加0.5是因为采样点坐标范围是[0,1]

                //从体数据中采样
                vec4 sample;
                if(sampleType<0.5)
                    sample=SMP3D_Near(smpPos.x,smpPos.y,smpPos.z);
                else
                    sample=SMP3D_Line(smpPos.x,smpPos.y,smpPos.z);

                float s_data = sample.w;                                //目前我们的体数据的第四个通道是存储解刨数据灰度值

                //注意：这里从13版本开始变为从传递函数中获得颜色与不透明度
                vec3 s_color = texture2D(TransFuncData,vec2(s_data,0.5)).xyz;                       //基础颜色
                float s_alpha = texture2D(TransFuncData,vec2(s_data,0.5)).w*step*GlobalOpacity;     //基础不透明度

                //以下是一些特殊处理：
                vec3 grad = sample.xyz*2.0-vec3(1.0,1.0,1.0);
                vec3 norm =normalize(-grad);
                float light = Light(norm,NowPos);
                if(SurfaceIncreaseEnable>0.5)
                    s_alpha*=SurfaceIncreaseFunc(grad);
                if(LightEnable>0.5)
                    s_color*=light;
                if(ContPreEnable>0.5)
                    s_alpha*=ContPre(grad,light,distance(NowPos,EyePos),AccumulateColor.w);
                if(FresnelEnable>0.5)
                    s_color = Fresnel(s_color,norm,Dir);

                //颜色叠加的核心算法
                AccumulateColor.xyz +=(1.0-AccumulateColor.w)*s_color*s_alpha;
                AccumulateColor.w+=(1.0-AccumulateColor.w)*s_alpha;

                if(AccumulateColor.w>= 1.0)          //如果叠加的不透明度已经到达1了则停止计算
                {
                    AccumulateColor.w = 1.0;
                    break;
                }
                NowPos += Dir*step;                 //把当前的位置向着视线方向移动一个步长的距离
                near+=step;                         //这个操作是为了判断是否当前位置是否走出了体数据立方体
                if(near > far)
                    break;
            }

			
			vec3 finalColor = AccumulateColor.xyz*AccumulateColor.w+debug_BackGroundColor*(1.0-AccumulateColor.w);
            gl_FragColor = vec4(finalColor,1.0);
        }
    </script>

</head>
<body>
    <style type="text/css">
        div.BlockTypeA { margin-left:auto;margin-right:auto;width: 90%;height: auto;background-color: brown;padding: 8px}
        div.fold{width: 257px;height: auto;background-color:bisque ;padding: 8px}
        input.foldBtn{width: 100%;}
        hr.DivideLine{width: 100%}
    </style>
    <div id="LeftDiv" style="width: 320px;background-color: coral;position: absolute;left: 8px;top: 8px;overflow-y:scroll;">
        <p><div class="BlockTypeA">
            <input type="button" class="foldBtn" value="导入体数据" onclick="ui_FoldIdv(1)">
            <div class="fold" id="Fold_1">
                数据类型：
                <select>
                    <option value="0">体数据</option>
                    <option value="1">电压体数据</option>
                </select>
                <hr class="DivideLine">
                数据尺寸（宽度，长度，厚度）
                <br><input type="number" id="RawFileWidth" value="256" style="width: 60px "/> <input type="number" id="RawFileHeight" value="256" style="width: 60px"/> <input type="number" id="RawFileDepth" value="256" style="width: 60px"/>
                <hr class="DivideLine">
                    二维贴图排布方案：<input type="button" value="自动选择">
                    <br>宽度：<select id="VolumeTexWidth">
                        <option value="2">2</option>
                        <option value="4">4</option>
                        <option value="8">8</option>
                        <option value="16">16</option>
                        <option value="32">32</option>
                        <option value="64">64</option>
                        <option value="128">128</option>
                        <option value="256">256</option>
                        <option value="512">512</option>
                        <option value="1024">1024</option>
                        <option value="2048">2048</option>
                        <option value="4096">4096</option>
                        <option value="8192">8192</option>
                        <option value="16384">16384</option>
                    </select>
                        高度：<select id="VolumeTexHeight">
                        <option value="2">2</option>
                        <option value="4">4</option>
                        <option value="8">8</option>
                        <option value="16">16</option>
                        <option value="32">32</option>
                        <option value="64">64</option>
                        <option value="128">128</option>
                        <option value="256">256</option>
                        <option value="512">512</option>
                        <option value="1024">1024</option>
                        <option value="2048">2048</option>
                        <option value="4096">4096</option>
                        <option value="8192">8192</option>
                        <option value="16384">16384</option>
                    </select>

                <hr class="DivideLine">
                <input type="file" id="VolumeRawFile">
                <hr class="DivideLine">
                <input type="button" value="开始导入！" style="width: 100%" onclick="readVolumeRaw()">
                <div style="width: 100%;height: 32px;background-color: cadetblue;">
                    <div id="LoadFileProgress" style="width: 100%; height: 24px;top: 4px;position:relative;background-color: aquamarine"></div>
                </div>
            </div>
         </div></p>
        <p><div class="BlockTypeA">
        <input type="button"class="foldBtn" value="设置实时/非实时渲染" onclick="ui_FoldIdv(0)">
        <div class="fold" id="Fold_0">
            <input type="radio" id="tetetet" name="RenderRealTimeType" onchange="ui_ChangeRealTimeType(0)"/>实时渲染<br>
            <hr class="DivideLine">
            <input type="radio" name="RenderRealTimeType" onchange="ui_ChangeRealTimeType(1)"/>非实时渲染<br>
            <input type="button" id="RenderThisFrame" disabled="disabled" style="width: 100%" value="渲染当前帧" onclick="ui_RenderThisFrame()">
        </div>
    </div></p>
        <p><div class="BlockTypeA">
            <input type="button" class="foldBtn" value="设置渲染质量" onclick="ui_FoldIdv(2)">
            <div class="fold" id="Fold_2">
                渲染视图尺寸：<br>
                <input id="RenderSizeInputField" type="number" value="512"  min="64" max="2048" />
                <input type="button" value="确认修改" onclick="ui_setRenderSize()"/>
                <hr class="DivideLine">
                步数：<br>
                <input type="number" id="stepText" min="10" max="2000" style="width:60px" oninput="ui_setStep(0)">
                <input type="range" id="stepSlider" min="10" max="2000" value="100" style="width: 120px" oninput="ui_setStep(1)">
                <hr class="DivideLine">
                z方向纹理采样方法：<br>
                <select id="SampleType" onchange="ui_setSampleType()">
                <option value="0">临近采样</option>
                <option value="1">线性采样</option>
                </select>
            </div>
        </div> </p>
        <p><div class="BlockTypeA">
        <input type="button" class="foldBtn" value="设置全局渲染变量" onclick="ui_FoldIdv(4)">
        <div class="fold" id="Fold_4">
            全局不透明度调整：<label id="GlobalOpacityLabel"></label><br>
            <input type="number" id="GlobalOpacityText" min="-80" max="80" style="width:60px" oninput="ui_setGlobalOpacity(0)">
            <input type="range" id="GlobalOpacitySlider" min="-80" max="80" value="33" style="width: 120px" oninput="ui_setGlobalOpacity(1)">
        </div>
    </div> </p>
    </div>
    <div id="RightDiv" style="width: 320px;background-color: khaki;position: absolute;top: 8px">
        <p><div class="BlockTypeA">
            <input type="checkbox" onchange="ui_FoldIdv(5)">启用梯度相关表面增强<br>
            <div id="Fold_5">
                不透明度系数：<input id="SurfaceIncreaseValue" type="range" min="0" max="500" value="100" oninput="ui_ModifySurfaceIncreaseValue()">
            </div>
        </div> </p>
        <p><div class="BlockTypeA">
        <input type="checkbox" onchange="ui_FoldIdv(6)">启用光照<br>
        <div id="Fold_6">
             环境光光强： <input id="LightIntensity0" type="range" min="0" max="200" value="50" oninput="ui_ModifyLightIntensity(0)"><br>
             漫反射光强： <input id="LightIntensity1" type="range" min="0" max="1000" value="500" oninput="ui_ModifyLightIntensity(1)"><br>
            镜面反射光强：<input id="LightIntensity2" type="range" min="0" max="1000" value="500" oninput="ui_ModifyLightIntensity(2)"><br>
            <input type="checkbox" onchange="ui_FoldIdv(8)">自定义光源位置（否则光源将将永远在视点处）<br>
            <div id="Fold_8">
                光照方向方位角：<input id="LightRotY" type="range" min="0" max="360" value="90" oninput="ui_ModifyLightPos()"><br>
                光照方向仰角：<input id="LightRotX" type="range" min="0" max="360" value="0" oninput="ui_ModifyLightPos()"><br>
            </div>
        </div>
        </div> </p>
        <p><div class="BlockTypeA">
        <input type="checkbox" onchange="ui_FoldIdv(7)">启用环境保留效果<br>
        <div id="Fold_7">
            Kt： <input id="ContPreKt" type="range" min="0" max="100" value="10" oninput="ui_ModifyContPreKtKs()"><br>
            Ks： <input id="ContPreKs" type="range" min="0" max="100" value="10" oninput="ui_ModifyContPreKtKs()"><br>
        </div>
        </div> </p>
        <p><div class="BlockTypeA">
        <input type="checkbox" onchange="ui_FoldIdv(9)">启用菲涅尔边缘效果<br>
        <div id="Fold_9">
            菲涅尔边缘颜色：<input id="FresnelColor" type="color" onchange="ui_ModifyFresnel()"><br>
            菲涅尔边缘系数：<input id="FresnelValue" type="range" min="0" max="100" value="50" oninput="ui_ModifyFresnel()">
        </div>
        </div> </p>
    </div>
    <div id="BtmDiv" style="height: 320px;background-color: #00b7ff;position: absolute;left: 8px;">
        <div id="BtmLeftDiv" style="width: 320px;height:320px; background-color: deeppink; position: absolute;left: 0px;top:0px;overflow-y: scroll;" >
            <p><div class="BlockTypeA">
            <input type="button" class="foldBtn" value="设置直方图显示" onclick="ui_FoldIdv(3)">
            <div class="fold" id="Fold_3">
                内芯宽度：<input  id="HB_inner" type="range"  min="1" max="8" value="6" oninput="ui_ModifyHistogram()"/>
                <br>边框宽度：<input  id="HB_border" type="range"  min="0" max="4" value="2"  oninput="ui_ModifyHistogram()"/>
                <br>间隔宽度：<input  id="HB_blank" type="range"  min="0" max="12" value="4"  oninput="ui_ModifyHistogram()"/>
                <hr class="DivideLine">
                颜色：<select id="HB_ColorType" onchange="ui_ModifyHistogram()"><option value="0">自定义</option><option value="1">根据传递函数颜色</option></select><br>
                <div id="HB_ColorDiv">
                    内芯：<input type="color" id="HB_innerColor" value="#00ffff" onchange="ui_ModifyHistogram()">
                    边框：<input type="color" id="HB_borderColor" onchange="ui_ModifyHistogram()">
                </div>
                <hr class="DivideLine">
                直方图显示高度：<br>
                <input value=" -10 " type="button" onclick="ui_ModifyHistogramHeight(-10)">
                <input value=" -1 " type="button" onclick="ui_ModifyHistogramHeight(-1)">
                <input value="0" type="number" id="HistogramHeightValue" style="width: 40px"min="0" oninput="ui_ModifyHistogramHeight(0)">
                <input value=" +1 " type="button" onclick="ui_ModifyHistogramHeight(1)">
                <input value=" +10 " type="button" onclick="ui_ModifyHistogramHeight(10)">
                <hr class="DivideLine">
                <input type="button" value="恢复默认设置" style="width: 100%" onclick="ui_HistogramDefaultSet()">
            </div>
        </div></p>
        </div>
        <div id="BtmMidDiv" style="left: 328px;  width: 800px;height: 312px;top:8px; background-color: blueviolet; overflow-x: scroll;overflow-y: hidden;position: absolute" >
            <canvas id="HistogramCanvas" width="1500px" height="200px" style="background-color: white;" ></canvas>
            <canvas id="TransFuncTexCanvas" width="1500px" height="92px" style="background-color: white;position: absolute;top: 220px;left: 0px" ></canvas>
            <div id="TF_posBar" style="background-color: red;opacity: 0.6;width: 14px;height: 100%; position: absolute;top: 0px"></div>
        </div>
        <div id="BtmRightDiv" style="width: 304px;height:304px; position: absolute;top:0px; right:0px; background-color: aquamarine;padding: 8px">
            <label id="TF_MousePos">鼠标悬停位置:0</label>
            <hr class="DivideLine">
            <div id="TFPointDataDiv" style="background-color: #c7c7c7;width: 100%;line-height: 30px">
                <label id="TF_CursorPos">当前光标位置:0</label><label id="TF_IfCtrlPoint" style="right: 20px;position: absolute">非控制点</label><br>
                颜色:<input id="TF_pointColor" type="color" disabled="disabled" onchange="ui_TF_ModifyColor()"><br>
                不透明度:<input id="TF_pointOpacity" type="range" min="0" max="255" disabled="disabled" oninput="ui_TF_ModifyOpacity()"><br>
                <input id="TF_AddDelCtrlPoint" type="button" style="width: 100%;" value="添加控制点到当前位置" onclick="ui_TF_addPoint()">
            </div>
        </div>
    </div>

</body>
</html>